[org 0x1000]

dw 0x55aa; 魔数用来判断扇区对错

xchg bx,bx;

mov si,loader
call print


detect_memory:
    ;将EBX 清零 ，也就是初始化
    xor ebx,ebx
    
    ;es:di 结构体的缓存位置
    mov ax,0
    ;段寄存器，不能直接MOV 立即数
    mov es,ax
    mov edi,ards_buffer

    mov edx,0x534d4150 ;固定签名

.next
    ;子功能号
    mov eax,0xE820
    ;ARDS 数据字节数（BIT）
    mov ecx,20
    ;系统调用
    int 0x15
    ;判断返回值，是否置位
    jc error

    ;将缓存指针，指向下一个结构体
    add di,cx
    
    ;将结构体数量加一
    inc word [ards_count]
    ;如果ebx 为0 表示为最后一个ARDS结构 
    cmp ebx,0
    
    jnz .next

    mov si,detecting
    call print 
    ;mov byte [0xb8000],'p'\
    jmp prepare_protected_mode
     

print:
mov ah,0x0e ;将AH寄存器设置为0x0e，这是系统中断10的入口地址，用于将字符输出到屏幕。
;.next 将对应的字符以此打印
.next:          ;标记下一条指令的位置。
    mov al,[si] ;将当前的si 寄存器里的值移给al寄存器
    cmp al,0    ;将al 里的值与0作比较
    jz .done    ;如果为0 则跳到.done 处
    int 0x10    ;调用中断0x10，用于将AL寄存器中的字符输出到屏幕。
    inc si      ;如果上一条指令执行后没有产生进位（即上一个字符的输出没有出现错误），则将SI指针增加1，指向下一个字符的内存单元。
    jmp .next   ;跳转到.next标记处的指令，重复执行上述过程，直到遇到字符串结束标记。
.done:
    ret         ;返回调用程序，结束程序的执行。


loader:
    db "loader Onix...",10,13,0; \n\r ，这里的10 ，13 ，0 对应的是ASCII 表中的符号
detecting:
    db "Detecting Memory Success ...",10,13,0; \n\r ，这里的10 ，13 ，0 对应的是ASCII 表中的符号

error:
    mov si,.msg
    call print
    hlt;让CPU停止
    jmp $
    .msg db "loader ERROR !!!",10,13,0

;-----------------------保护模式汇编代码------------------------------
prepare_protected_mode:
    xchg bx,bx;断点

    cli; 进入保护模式之后就不允许中断了

    ;打开A20总线
    in al,0x92;
    or al,0b10
    out 0x92,al
    ;加载 GDT
    lgdt [gdt_ptr];加载GDT

    ;启动保护模式 将cro 寄存器置位1
    mov eax,cr0 
    or eax,1
    mov cr0,eax

    ;用跳转来刷新缓存，启用保护模式 
    ;通过段选择子访问内存空间：code_selector:protect_mode
    jmp dword code_selector:protect_mode

[bits 32]



protect_mode:
    ;将除了代码段之外的寄存器全部设位数据段
    xchg bx,bx;
    mov ax,data_selector
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ss,ax;初始化段寄存器
    mov esp,0x10000;修改栈顶
  
    mov byte[0xb8000],'p'

    xchg bx,bx
    mov edi,0x10000; 读取的目标的内存  也就将指针移动到要操作的位
    mov ecx,10;      起始地址            
    mov bl,200;       扇区数量  bl 8位寄存器

    call read_disk;

    jmp dword code_selector:0x10000
    
    ud2
  ;
jmp $;

memory_base equ 0;内存开始的位置， 基地址
memory_limit equ ((1024*1024*1024*4)/(1024*4))-1; 内存界限 4G/4K (内存的长度)
;段选择子
code_selector equ (1<<3) 
data_selector equ (2<<3)


read_disk:

    ;读出扇区

    ;设置读写扇区的数量
    mov dx,0x1f2
    mov al,bl
    out dx,al ;out 是输出指令 AL的值（即BL的值）将被写入到端口地址0x1f2。
    
    inc dx; 0x1f3 inc 是 自加指令
    mov al,cl ;起始扇区的低八位
    out dx,al 
    
    inc dx; 0x1f4 
    shr ecx,8 ;寄存器ECX的值逻辑右移8位 ,将低8位清空
    mov al,cl ;起始扇区的中八位
    out dx,al 

    inc dx; 0x1f5 
    shr ecx,8 ;寄存器ECX的值逻辑右移8位，将中低8位清空
    mov al,cl ;起始扇区的高八位
    out dx,al 
    
    inc dx; 0x1f6 
    shr ecx,8 ;寄存器ECX的值逻辑右移8位，将高低8位清空
    and cl,0b1111; 经cl寄存器里边的高四位内容

    mov al,0b1110_0000;
    or al,cl
    out dx,al ;主盘，- LBA 模式

    inc dx; 0x1f7
    mov al,0x20
    out dx,al

    xor ecx,ecx;
    mov cl,bl; 得到读写硬盘的数量

    .read:
        push cx;保存CX
        call .waits;等待数据准备完成
        call .reads;读取一个扇区
        pop cx; 恢复cx
        loop .read

    ret

    .waits:
        mov dx,0x1f7
            .check:
                in al,dx
                jmp $+2 ; nop 直接跳转到下一行
                jmp $+2 ; 一点点延时
                jmp $+2
                and al, 0b1000_1000
                cmp al, 0b0000_1000
                jnz .check
    ret

    .reads:
        mov dx,0x1f0
        mov cx,256;
        .readw:
                in ax,dx
                jmp $+2 ; nop 直接跳转到下一行
                jmp $+2 ; 一点点延时
                jmp $+2
                mov [edi],ax
                add edi,2
            loop .readw
    ret

Write_disk:

    ;写入扇区

    ;设置读写扇区的数量
    mov dx,0x1f2
    mov al,bl
    out dx,al ;out 是输出指令 AL的值（即BL的值）将被写入到端口地址0x1f2。
    
    inc dx; 0x1f3 inc 是 自加指令
    mov al,cl ;起始扇区的低八位
    out dx,al 
    
    inc dx; 0x1f4 
    shr ecx,8 ;寄存器ECX的值逻辑右移8位 ,将低8位清空
    mov al,cl ;起始扇区的中八位
    out dx,al 

    inc dx; 0x1f5 
    shr ecx,8 ;寄存器ECX的值逻辑右移8位，将中低8位清空
    mov al,cl ;起始扇区的高八位
    out dx,al 
    
    inc dx; 0x1f6 
    shr ecx,8 ;寄存器ECX的值逻辑右移8位，将高低8位清空
    and cl,0b1111; 经cl寄存器里边的高四位内容

    mov al,0b1110_0000;
    or al,cl
    out dx,al ;主盘，- LBA 模式

    inc dx; 0x1f7  0x1f7硬盘状态
    mov al,0x30;设置为写入模式
    out dx,al

    xor ecx,ecx;
    mov cl,bl; 得到读写硬盘的数量

    .Write:
        push cx;保存CX
        call .Writes;写一个扇区
        call .Waits;等待硬盘繁忙结束
        pop cx; 恢复
        loop .Write

    ret

    .Waits:
        mov dx,0x1f7
            .check:
                in al,dx
                jmp $+2 ; nop 直接跳转到下一行
                jmp $+2 ; 一点点延时
                jmp $+2
                and al, 0b1000_0000
                cmp al, 0b0000_0000
                jnz .check
    ret

    .Writes:
        mov dx,0x1f0
        mov cx,256;写入一个字节512
        .Writew:
                mov ax,[edi];将目标寄存器的地址给ax
                out dx,ax;将AX的值给dx
                jmp $+2 ; nop 直接跳转到下一行
                jmp $+2 ; 一点点延时
                jmp $+2
                add edi,2
            loop .Writew
    ret




;描述符指针
gdt_ptr:
    dw (gdt_end - gdt_base)-1
    dd gdt_base


;--------------------全局描述符汇编代码------------------------------
gdt_base:
    dd 0,0 ;NULL 描述符

gdt_code:

    dw memory_limit & 0xffff;段界限的0~15位
    dw memory_base & 0xffff;基地址0~16位 
    db (memory_base >> 16 ) & 0xff  ;基地址0~16位
    ;存在内存还是硬盘-dlp-段类型-代码段--依从？-可读？-访问？
    db 0b_1_00_1_1_0_1_0
    ;4k?-32?-64标志-留存位-段界限16~19位
    db 0b1_1_0_0_0000|(memory_limit >> 16) & 0xf
    db (memory_base >> 24) & 0xff;

gdt_data:

    dw memory_limit & 0xffff;段界限的0~15位
    dw memory_base & 0xffff;基地址0~16位 
    db (memory_base >> 16 ) & 0xff  ;基地址0~16位
    ;存在内存还是硬盘-dlp-段类型-数据段--向上扩展-可读？-访问？
    db 0b_1_00_1_0_0_1_0
    ;4k?-32?-64标志-留存位-段界限16~19位
    db 0b1_1_0_0_0000|(memory_limit >> 16) & 0xf
    db (memory_base >> 24) & 0xff;




gdt_end:


ards_count:
    dw 0

ards_buffer:

